package thinkinjava1.ch12;

/**
 * Created by BobbyGu on 2017/2/24.
 */
// Checking to see if a handle can be cloned
// Can't clone this because it doesn't
// override clone():
class Ordinary {
}

// Overrides clone, but doesn't implement
// Cloneable:
class WrongClone extends Ordinary {
    public Object clone()
            throws CloneNotSupportedException {
        return super.clone(); // Throws exception
    }
}

// Does all the right things for cloning:
class IsCloneable extends Ordinary
        implements Cloneable {
    public Object clone()
            throws CloneNotSupportedException {
        return super.clone();
    }
}

// Turn off cloning by throwing the exception:
class NoMore extends IsCloneable {
    public Object clone()
            throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }
}

class TryMore extends NoMore {
    public Object clone()
            throws CloneNotSupportedException {
// Calls NoMore.clone(), throws exception:
        return super.clone();
    }
}

class BackOn extends NoMore {
    private BackOn duplicate(BackOn b) {
// Somehow make a copy of b
// and return that copy. This is a dummy
// copy, just to make the point:
        return new BackOn();
    }

    public Object clone() {
// Doesn't call NoMore.clone():
        return duplicate(this);
    }
}

// Can't inherit from this, so can't override
// the clone method like in BackOn:
final class ReallyNoMore extends NoMore {
}

public class CheckCloneable {
    static Ordinary tryToClone(Ordinary ord) {
        String id = ord.getClass().getName();
        Ordinary x = null;
        if (ord instanceof Cloneable) {
            try {
                System.out.println("Attempting " + id);
                x = (Ordinary) ((IsCloneable) ord).clone();
                System.out.println("Cloned " + id);
            } catch (CloneNotSupportedException e) {
                System.out.println(
                        "Could not clone " + id);
            }
        }
        return x;
    }

    /**
     * 总之，如果希望一个类能够克隆，那么：
     * (1) 实现 Cloneable 接口
     * (2) 覆盖 clone()
     * (3) 在自己的 clone()中调用 super.clone()
     * (4) 在自己的 clone()中捕获违例
     * 这一系列步骤能达到最理想的效果。
     *
     * @param args
     */
    public static void main(String[] args) {
// Upcasting:
        Ordinary[] ord = {
                new IsCloneable(),
                new WrongClone(),
                new NoMore(),
                new TryMore(),
                new BackOn(),
                new ReallyNoMore(),
        };
        Ordinary x = new Ordinary();
        // This won't compile, since clone() is
        // protected in Object:
        //! x = (Ordinary)x.clone();
        // tryToClone() checks first to see if
        // a class implements Cloneable:
        for (int i = 0; i < ord.length; i++)
            tryToClone(ord[i]);
    }
}
